#include "sys.h"
/**************************************************************************
Function: Set the vector table offset address
Input   : Base site 	 Offsets
Output  : none
函数功能：设置向量表偏移地址
入口参数：NVIC_VectTab:基址		Offset:偏移量
返回  值：无
**************************************************************************/
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)
{
    SCB->VTOR = NVIC_VectTab | (Offset & (u32)0x1FFFFF80);  // 设置NVIC的向量表偏移寄存器
                                                            // 用于标识向量表是在CODE区还是在RAM区
}
/**************************************************************************
Function: Set NVIC group
Input   : NVIC_Group
Output  : none
函数功能：设置中断分组
入口参数：NVIC_Group:NVIC分组 0~4 总共5组
返回  值：无
**************************************************************************/
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)
{
    u32 temp, temp1;
    temp1 = (~NVIC_Group) & 0x07;  // 取后三位
    temp1 <<= 8;
    temp = SCB->AIRCR;   // 读取先前的设置
    temp &= 0X0000F8FF;  // 清空先前分组
    temp |= 0X05FA0000;  // 写入钥匙
    temp |= temp1;
    SCB->AIRCR = temp;  // 设置分组
}
/**************************************************************************
Function: Set NVIC group
Input   : NVIC_PreemptionPriority	NVIC_SubPriority	NVIC_Channel	NVIC_Group
Output  : none
函数功能：设置中断分组
入口参数：抢占优先级	响应优先级 中断编号	中断分组号
返回  值：无
* 中断分组 | 作用
 * --------+--------------------------------------
 * 组0     | 0位抢占优先级,4位响应优先级
 * 组1     | 1位抢占优先级,3位响应优先级
 * 组2     | 2位抢占优先级,2位响应优先级
 * 组3     | 3位抢占优先级,1位响应优先级
 * 组4     | 4位抢占优先级,0位响应优先级
 *NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先
**************************************************************************/
void MY_NVIC_Init(u8 NVIC_PreemptionPriority, u8 NVIC_SubPriority, u8 NVIC_Channel, u8 NVIC_Group)
{  // 注意优先级不能超过设定的组的范围!否则会有意想不到的错误
    u32 temp;
    MY_NVIC_PriorityGroupConfig(NVIC_Group);  // 设置分组
    temp = NVIC_PreemptionPriority << (4 - NVIC_Group);
    temp |= NVIC_SubPriority & (0x0f >> NVIC_Group);
    temp &= 0xf;                                                // 取低四位
    NVIC->ISER[NVIC_Channel / 32] |= (1 << NVIC_Channel % 32);  // 使能中断位(要清除的话,相反操作就OK)
    NVIC->IP[NVIC_Channel] |= temp << 4;                        // 设置响应优先级和抢断优先级
}
/**************************************************************************
Function: External interrupt function configuration
Input   : GPIOx：General-purpose input/output	 BITx：The port needed enable
                    TRIM：Trigger mode
Output  : none
函数功能：外部中断函数配置
入口参数：GPIOx:0~6,代表GPIOA~G；
                    BITx:需要使能的位;		TRIM:触发模式,1,下升沿;2,上降沿;3，任意电平触发
返回  值：无
**************************************************************************/
// 该函数一次只能配置1个IO口,多个IO口,需多次调用
// 该函数会自动开启对应中断,以及屏蔽线
void Ex_NVIC_Config(u8 GPIOx, u8 BITx, u8 TRIM)
{
    u8 EXTADDR;
    u8 EXTOFFSET;
    EXTADDR = BITx / 4;  // 得到中断寄存器组的编号
    EXTOFFSET = (BITx % 4) * 4;
    RCC->APB2ENR |= 0x01;                             // 使能io复用时钟
    AFIO->EXTICR[EXTADDR] &= ~(0x000F << EXTOFFSET);  // 清除原来设置！！！
    AFIO->EXTICR[EXTADDR] |= GPIOx << EXTOFFSET;      // EXTI.BITx映射到GPIOx.BITx
    // 自动设置
    EXTI->IMR |= 1 << BITx;                    //  开启line BITx上的中断
                                               // EXTI->EMR|=1<<BITx;//不屏蔽line BITx上的事件 (如果不屏蔽这句,在硬件上是可以的,但是在软件仿真的时候无法进入中断!)
    if (TRIM & 0x01) EXTI->FTSR |= 1 << BITx;  // line BITx上事件下降沿触发
    if (TRIM & 0x02) EXTI->RTSR |= 1 << BITx;  // line BITx上事件上升降沿触发
}
/**************************************************************************
Function: Reset all clock registers
Input   : none
Output  : none
函数功能：把所有时钟寄存器复位
入口参数：无
返回  值：无
**************************************************************************/
// 不能在这里执行所有外设复位!否则至少引起串口不工作.
// 把所有时钟寄存器复位
void MYRCC_DeInit(void)
{
    RCC->APB1RSTR = 0x00000000;  // 复位结束
    RCC->APB2RSTR = 0x00000000;

    RCC->AHBENR = 0x00000014;   // 睡眠模式闪存和SRAM时钟使能.其他关闭.
    RCC->APB2ENR = 0x00000000;  // 外设时钟关闭.
    RCC->APB1ENR = 0x00000000;
    RCC->CR |= 0x00000001;    // 使能内部高速时钟HSION
    RCC->CFGR &= 0xF8FF0000;  // 复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]
    RCC->CR &= 0xFEF6FFFF;    // 复位HSEON,CSSON,PLLON
    RCC->CR &= 0xFFFBFFFF;    // 复位HSEBYP
    RCC->CFGR &= 0xFF80FFFF;  // 复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
    RCC->CIR = 0x00000000;    // 关闭所有中断
    // 配置向量表
#ifdef VECT_TAB_RAM
    MY_NVIC_SetVectorTable(0x20000000, 0x0);
#else
    MY_NVIC_SetVectorTable(0x08000000, 0x0);
#endif
}
// THUMB指令不支持汇编内联
// 采用如下方法实现执行汇编指令WFI
__asm void WFI_SET(void) { WFI; }
// 关闭所有中断
__asm void INTX_DISABLE(void) { CPSID I; }
// 开启所有中断
__asm void INTX_ENABLE(void) { CPSIE I; }
// 设置栈顶地址
// addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
    MSR MSP, r0  // set Main Stack value
                 BX r14
}

/**************************************************************************
Function: Go to standby mode
Input   : none
Output  : none
函数功能：进入待机模式
入口参数：无
返回  值：无
**************************************************************************/
void Sys_Standby(void)
{
    SCB->SCR |= 1 << 2;       // 使能SLEEPDEEP位 (SYS->CTRL)
    RCC->APB1ENR |= 1 << 28;  // 使能电源时钟
    PWR->CSR |= 1 << 8;       // 设置WKUP用于唤醒
    PWR->CR |= 1 << 2;        // 清除Wake-up 标志
    PWR->CR |= 1 << 1;        // PDDS置位
    WFI_SET();                // 执行WFI指令
}
/**************************************************************************
Function: System soft reset
Input   : none
Output  : none
函数功能：系统软复位
入口参数：无
返回  值：无
**************************************************************************/
void Sys_Soft_Reset(void) { SCB->AIRCR = 0X05FA0000 | (u32)0x04; }
/**************************************************************************
Function: Set JTAG mode
Input   : mode:JTAG, swd mode settings；00，all enable；01，enable SWD；10，Full shutdown
Output  : none
函数功能：设置JTAG模式
入口参数：mode:jtag,swd模式设置;00,全使能;01,使能SWD;10,全关闭;
返回  值：无
**************************************************************************/
// #define JTAG_SWD_DISABLE   0X02
// #define SWD_ENABLE         0X01
// #define JTAG_SWD_ENABLE    0X00
void JTAG_Set(u8 mode)
{
    u32 temp;
    temp = mode;
    temp <<= 25;
    RCC->APB2ENR |= 1 << 0;    // 开启辅助时钟
    AFIO->MAPR &= 0XF8FFFFFF;  // 清除MAPR的[26:24]
    AFIO->MAPR |= temp;        // 设置jtag模式
}
/**************************************************************************
Function: System clock initialization function
Input   : pll：Selected frequency multiplication，Starting at 2, the maximum value is 16
Output  : none
函数功能：系统时钟初始化函数
入口参数：pll:选择的倍频数，从2开始，最大值为16
返回  值：无
**************************************************************************/
void Stm32_Clock_Init(u8 PLL)
{
    unsigned char temp = 0;
    MYRCC_DeInit();            // 复位并配置向量表
    RCC->CR |= 0x00010000;     // 外部高速时钟使能HSEON
    while (!(RCC->CR >> 17));  // 等待外部时钟就绪
    RCC->CFGR = 0X00000400;    // APB1=DIV2;APB2=DIV1;AHB=DIV1;
    PLL -= 2;                  // 抵消2个单位
    RCC->CFGR |= PLL << 18;    // 设置PLL值 2~16
    RCC->CFGR |= 1 << 16;      // PLLSRC ON
    FLASH->ACR |= 0x32;        // FLASH 2个延时周期

    RCC->CR |= 0x01000000;     // PLLON
    while (!(RCC->CR >> 25));  // 等待PLL锁定
    RCC->CFGR |= 0x00000002;   // PLL作为系统时钟
    while (temp != 0x02)       // 等待PLL作为系统时钟设置成功
    {
        temp = RCC->CFGR >> 2;
        temp &= 0x03;
    }
}
